require 'digest'
require 'json'

# A simple Virtual Private Computer written in ruby.
# This is the main (and, currently, only) Class.
# This class supports chaining 👍

class Computer
  # Create a new Computer object
  #
  # @param username [String] The username to create the new Computer with
  # @param password [String] The password to assign to the username, the password's SHA and MD5 are what's stored
  # @return [Computer] A brand-spankin'-new Computer object, with username and password
  def initialize(username, password)
    @username = username
    @sha = Digest::SHA256.hexdigest password
    @md5 = Digest::MD5.hexdigest password
    @files = Hash.new
    @@users[username.to_sym] = Hash.new
    @@users[username.to_sym][:sha] = @sha
    @@users[username.to_sym][:md5] = @md5
  end

  private
  @@users = Hash.new(Hash.new(0))
  @@gfiles = Hash.new(Hash.new(0))

  public
  # Create a new file for the current user
  #
  # @param filename [String] The filename for the new file
  # @param contents [String] The contents to dump into the new file
  # @return [Computer] Returns the current object (for chaining)
  def create(filename, contents)
    filename = filename.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_')
    @files[filename] = Hash.new
    @files[filename]["contents"] = contents
    @files[filename]["time"] = Time.now
    self
  end

  # Create a new global file, accessible by all users
  #
  # @param password [String] Used sort of like +sudo+ for writing to +/+
  # @param (see #create)
  # @return (see #create)
  def create_global(password, filename, contents)
    filename = filename.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_')
    if (Digest::SHA256.hexdigest password) == @sha && (Digest::MD5.hexdigest password) == @md5
      @@gfiles[filename] = Hash.new
      @@gfiles[filename]["creator"] = @username
      @@gfiles[filename]["contents"] = contents
      @@gfiles[filename]["time"] = Time.now
    else
      puts "Invalid password"
    end
    self
  end

  # Return the username for the current user
  #
  # @return [String] The current user's username
  def user
    @username unless @username.nil?
  end

  # Deletes a file from the user's fs
  #
  # @return (see #create)
  def delete(filename)
    filename = filename.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_')
    @files.delete(filename) if @files.has_key?(filename)
    self
  end

  # Returns the contents of the specified file from the user's fs
  #
  # @param filename [String] The filename of the file to return
  # @return [String] The contents of the selected file
  def get(filename)
    filename = filename.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_')
    @files[filename]["contents"] if @files.has_key?(filename).has_key?("contents")
  end

  # Gets the Hash containing all the users
  #
  # @return [Hash] Hash containing all the users
  def self.get_users
    @@users unless @@users.nil?
  end

  # Gets the Hash containing all the global files
  #
  # @return [Hash] Hash containing all the global files
  def self.get_gfiles
    @@gfiles unless @@gfiles.nil?
  end

  # Gets the specified global file, can also be used with a
  # yield block
  #
  # @param filename [String] The filename of the global file to return
  # @return [Hash, Computer] If a block is given, it returns +self+, otherwise it returns the file Hash
  # @yield [creator, contents, time] Optional yield block for the file
  def self.gfile(filename)
    filename = filename.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_')
    yield(@@gfiles[filename]["creator"], @@gfiles[filename]["contents"], @@gfiles[filename]["time"]) if block_given?
    @@gfiles[filename] unless block_given?
    self unless block_given?
  end

  # Saves all the global files to a JSON file
  #
  # @param file [String] The relative path of the file, including the filename
  # @return (see #create)
  def self.gsave(file)
    file = file.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_')
    File.open(file, "w") do |f|
      f.write(JSON.pretty_generate(@@gfiles))
    end
  rescue
    puts "Error writing."
  ensure
    self
  end

  # Loads the specified JSON file into the global files
  #
  # @note This replaces the current global files, so be careful!
  # @param username [String] +sudo+-like verification
  # @param password [String] +sudo+-like verification
  # @return (see #create)
  def self.gopen(username, password, file)
    file = file.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_')
    if (Digest::SHA256.hexdigest password) == @@users[username.to_sym][:sha] && (Digest::MD5.hexdigest password) == @@users[username.to_sym][:md5]
      @@gfiles = JSON.parse(File.read(file)) if File.exists?(file)
    else
      puts "Invalid password."
    end
  rescue
    puts "Error reading."
  ensure
    self
  end

  # Do the given block for each file in the global files
  #
  # @yield [creator, filename, contents, time] Yield block to do for each file
  # @return (see #create)
  def self.geach
    @@gfiles.each do |f, h|
      yield(h["creator"], f, h["contents"], h["time"]) if block_given?
      puts "#{f}: #{h}" unless block_given?
    end
    self
  end

  # Do the given block for each file in the user's fs
  #
  # @yield [filename, contents, time] Yield block to do for each file
  # @return (see #create)
  def each
    @files.each do |f, h|
      yield(f, h["contents"], h["time"]) if block_given?
      puts "#{f}: #{h}" unless block_given?
    end
  end
end
